package com.yourenbang.lingyun.transaction.web.service.impl.fund.fundopt;

import com.yourenbang.lingyun.constants.BizCodeMessages;
import com.yourenbang.lingyun.constants.enums.PlatNoEnums;
import com.yourenbang.lingyun.core.message.Result;
import com.yourenbang.lingyun.core.util.BizAssertUtils;
import com.yourenbang.lingyun.core.util.DingdingUtils;
import com.yourenbang.lingyun.core.util.KLog;
import com.yourenbang.lingyun.core.util.StringUtils;
import com.yourenbang.lingyun.domain.FundAccount;
import com.yourenbang.lingyun.transaction.config.DingdingConfig;
import com.yourenbang.lingyun.transaction.constant.ChangePlanStatus;
import com.yourenbang.lingyun.transaction.constant.JournalStatus;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountChangePlanMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountJournalMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountMapper;
import com.yourenbang.lingyun.transaction.web.domain.dto.ChangeFundAccountV2DTO;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountChangePlan;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountJournal;
import com.yourenbang.lingyun.transaction.web.service.fund.FundAccountChangePlanService;
import com.yourenbang.lingyun.transaction.web.service.fund.fundopt.FundOptAddService;
import com.yourenbang.transaction.api.model.dto.AddBillDTO;
import com.yourenbang.transaction.api.service.fund.FundBillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Objects;

@Slf4j
@Service
public class FundOptAddServiceImpl implements FundOptAddService {

    @Autowired
    private TransactionFundAccountMapper transactionFundAccountMapper;
    @Resource
    private TransactionFundAccountChangePlanMapper fundAccountChangePlanMapper;
    @Resource
    private FundAccountChangePlanService fundAccountChangePlanService;
    @Resource
    private DingdingConfig dingdingConfig;
    @Resource
    private FundBillService fundBillService;
    @Resource
    private TransactionFundAccountJournalMapper fundAccountJournalMapper;

    @Transactional
    @Override
    public Result add(FundAccount fromFundAccount, FundAccount toFundAccount, ChangeFundAccountV2DTO changeFundAccountV2DTO) {

        KLog.info("[通用账户加账]入参:{}", changeFundAccountV2DTO.toString());


        FundAccountChangePlan changePlan = fundAccountChangePlanMapper.getByOrderId(toFundAccount.getPlatNo(),
                changeFundAccountV2DTO.getOrderNo());



        if (Objects.nonNull(fromFundAccount)) {
            //检查资金发起方金额是否足够,fromFundAccount 为null说明是系统账号加帐,无需检查
            BigDecimal accountAmountWithdrawable = fromFundAccount.getAccountAmountWithdrawable();
            if (accountAmountWithdrawable.compareTo(changeFundAccountV2DTO.getChangeAmount()) < 0) {

                changePlan.setAccountChangePlanStatus(ChangePlanStatus.WAIT_TO_CHANGE);
                fundAccountChangePlanMapper.updateById(changePlan);

                sendDingdingAlarm(changeFundAccountV2DTO);

                return Result.builder().fail(BizCodeMessages.TRANSACTION_AMOUNT_DEFICIENCY);
            }
        }


        try {

            if (Objects.nonNull(fromFundAccount)) {
                //资金发起方 可提资金减少
                int i = transactionFundAccountMapper.withdrawableSubtract(fromFundAccount.getFundAccountId(),
                        changeFundAccountV2DTO.getChangeAmount());
                BizAssertUtils.assertOne(i, BizCodeMessages.TRANSACTION_AMOUNT_DEFICIENCY);


                //记录资金发起方流水
                FundAccountJournal journal = getFromFundAccountJournal(changeFundAccountV2DTO, fromFundAccount, changePlan);
                fundAccountJournalMapper.insert(journal);
            }


            //资金接收方 可提资金增加
            transactionFundAccountMapper.incomeAdd(toFundAccount.getFundAccountId(), changeFundAccountV2DTO.getChangeAmount());

            //记录资金接收起方流水
            FundAccountJournal toJournal = getToFundAccountJournal(changeFundAccountV2DTO, toFundAccount, changePlan);
            fundAccountJournalMapper.insert(toJournal);

            changePlan.setAccountChangePlanStatus(ChangePlanStatus.CHANGED);
            changePlan.setCreateTime(null);
            changePlan.setUpdateTime(null);
            fundAccountChangePlanMapper.updateById(changePlan);


            if(Objects.nonNull(fromFundAccount)) {
                addBill(changeFundAccountV2DTO, toFundAccount, changePlan);
            }

        } catch (Exception e) {

            KLog.error("通用加账错误:{},请求参数:{}", e.getMessage(), changeFundAccountV2DTO, e);

            //抓到异常,更新plan表为wait to change
            changePlan.setAccountChangePlanStatus(ChangePlanStatus.WAIT_TO_CHANGE);
            fundAccountChangePlanMapper.updateById(changePlan);

            return Result.builder().fail(BizCodeMessages.TRANSACTION_ADD_ACCOUNT_FAIL);

        }

        return Result.builder().success();
    }

    private void sendDingdingAlarm(ChangeFundAccountV2DTO changeFundAccountDTO) {
        //余额不足,钉钉预警
        String dingMark = "【账户余额不足预警】" + PlatNoEnums.getPlatNo(changeFundAccountDTO.getPlatNo()).getName() +
                changeFundAccountDTO.getJournalInfo().getFromAccountType().getAccountName() + "余额已不足,请尽快充值!";

        DingdingUtils.sendToDingding(dingMark, dingdingConfig.getDingdingWarnToken());
    }

    private void addBill(ChangeFundAccountV2DTO changeFundAccountV2DTO, FundAccount userAccountByType,
                         FundAccountChangePlan changePlan) {
        //账单
        AddBillDTO billDTO = AddBillDTO.builder()
                .userBaseId(changeFundAccountV2DTO.getUserBaseId())
                .fundAccountId(userAccountByType.getFundAccountId())
                .fundAccountChangePlanId(changePlan.getFundAccountChangePlanId())
                .orderId(changeFundAccountV2DTO.getOrderNo())
                .amount(changeFundAccountV2DTO.getChangeAmount())
                .billType(changeFundAccountV2DTO.getFundBillType())
                .channelId(changeFundAccountV2DTO.getChannelId())
                .detail(changeFundAccountV2DTO.getDetail())
                .showFlag(changeFundAccountV2DTO.getShowFlag())
                .platNo(changeFundAccountV2DTO.getPlatNo())
                .build();

        //手动操作
        if (StringUtils.isNotBlank(changeFundAccountV2DTO.getOperator())
                && Objects.nonNull(changeFundAccountV2DTO.getManualFlag())
                && changeFundAccountV2DTO.getManualFlag()) {
            billDTO.setOperator(changeFundAccountV2DTO.getOperator());
            billDTO.setManualFlag(changeFundAccountV2DTO.getManualFlag());
        }


        billDTO.setAccountMainJournalType(changePlan.getAccountMainJournalType());
        billDTO.setAccountMainJournalTypeName(changeFundAccountV2DTO.getJournalInfo().getAccountMainJournalType().getName());

        fundBillService.addBill(billDTO);
    }

    private FundAccountJournal getFromFundAccountJournal(ChangeFundAccountV2DTO changeFundAccountV2DTO, FundAccount fromFundAccount,
                                                         FundAccountChangePlan changePlan) {
        //变动之前可提现金额
        BigDecimal accountAmountWithdrawableBefore = fromFundAccount.getAccountAmountWithdrawable();
        //计算变动之后可提现金额
        BigDecimal accountAmountWithdrawableAfter =
                accountAmountWithdrawableBefore.subtract(changeFundAccountV2DTO.getChangeAmount());

        //变动之前已提现金额 发起方已提现金额增加
        BigDecimal accountAmountWithdrawedBefore = fromFundAccount.getAccountAmountWithdrawed();
        //计算变动后的已提现金额
        BigDecimal accountAmountWithdrawedAfter = accountAmountWithdrawedBefore.add(changeFundAccountV2DTO.getChangeAmount());


        FundAccountJournal journal = new FundAccountJournal(changeFundAccountV2DTO.getPlatNo());
        journal.setChannelId(changeFundAccountV2DTO.getChannelId());
        journal.setFundAccountChangePlanId(changePlan.getFundAccountChangePlanId());
        journal.setFundAccountId(fromFundAccount.getFundAccountId());
        journal.setJournalStatus(JournalStatus.SUCCESS);
        journal.setJournalChangeValue(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountJournalAcid(changePlan.getOrderId());
        journal.setAccountMainJournalType(changePlan.getAccountMainJournalType());
        journal.setAccountMainJournalTypeName(changeFundAccountV2DTO.getJournalInfo().getAccountMainJournalType().getName());


        //变动金额记录
        journal.setAccountIncome(BigDecimal.ZERO);
        journal.setAccountAmountFrozen(BigDecimal.ZERO);
        //可提现变动金额
        journal.setAccountAmountWithdrawable(changeFundAccountV2DTO.getChangeAmount());
        //已提现变动金额
        journal.setAccountAmountWithdrawed(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountAmountUndepositable(BigDecimal.ZERO);


        //变动前后记录
        //加帐情况,系统income收入无变化
        journal.setAccountIncomeBefore(fromFundAccount.getAccountIncome());
        journal.setAccountIncomeAfterwards(fromFundAccount.getAccountIncome());

        journal.setAccountAmountFrozenBefore(fromFundAccount.getAccountAmountFrozen());
        journal.setAccountAmountFrozenAfterwards(fromFundAccount.getAccountAmountFrozen());

        journal.setAccountAmountWithdrawableBefore(accountAmountWithdrawableBefore);
        journal.setAccountAmountWithdrawableAfterwards(accountAmountWithdrawableAfter);

        journal.setAccountAmountWithdrawedBefore(accountAmountWithdrawedBefore);
        journal.setAccountAmountWithdrawedAfterwards(accountAmountWithdrawedAfter);

        journal.setAccountAmountUndepositableBefore(fromFundAccount.getAccountAmountUndepositable());
        journal.setAccountAmountUndepositableAfterwards(fromFundAccount.getAccountAmountUndepositable());

        return journal;
    }

    private FundAccountJournal getToFundAccountJournal(ChangeFundAccountV2DTO changeFundAccountV2DTO, FundAccount toFundAccount,
                                                       FundAccountChangePlan changePlan) {

        BigDecimal toFundAccountAccountIncomeBefore = toFundAccount.getAccountIncome();
        BigDecimal toAccountAmtDrawableBe = toFundAccount.getAccountAmountWithdrawable();

        FundAccountJournal journal = new FundAccountJournal(changeFundAccountV2DTO.getPlatNo());
        journal.setChannelId(changeFundAccountV2DTO.getChannelId());
        journal.setFundAccountChangePlanId(changePlan.getFundAccountChangePlanId());
        journal.setFundAccountId(toFundAccount.getFundAccountId());
        journal.setJournalStatus(JournalStatus.SUCCESS);
        journal.setJournalChangeValue(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountJournalAcid(changePlan.getOrderId());

        //资金类型,大类
        journal.setAccountMainJournalType(changePlan.getAccountMainJournalType());
        journal.setAccountMainJournalTypeName(changeFundAccountV2DTO.getJournalInfo().getAccountMainJournalType().getName());

        //变动金额
        journal.setAccountIncome(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountAmountFrozen(BigDecimal.ZERO);
        journal.setAccountAmountWithdrawable(changeFundAccountV2DTO.getChangeAmount());
        journal.setAccountAmountWithdrawed(BigDecimal.ZERO);
        journal.setAccountAmountUndepositable(BigDecimal.ZERO);


        //账户收入变动前 接收方收入增加
        journal.setAccountIncomeBefore(toFundAccountAccountIncomeBefore);
        //计算账户收入变动后
        journal.setAccountIncomeAfterwards(toFundAccountAccountIncomeBefore.add(changeFundAccountV2DTO.getChangeAmount()));

        journal.setAccountAmountFrozenBefore(toFundAccount.getAccountAmountFrozen());
        journal.setAccountAmountFrozenAfterwards(toFundAccount.getAccountAmountFrozen());

        //变动之前可提现金额 接收方可提现金额增加
        journal.setAccountAmountWithdrawableBefore(toAccountAmtDrawableBe);
        //变动之后可提现金额
        journal.setAccountAmountWithdrawableAfterwards(toAccountAmtDrawableBe.add(changeFundAccountV2DTO.getChangeAmount()));

        journal.setAccountAmountWithdrawedBefore(toFundAccount.getAccountAmountWithdrawed());
        journal.setAccountAmountWithdrawedAfterwards(toFundAccount.getAccountAmountWithdrawed());

        journal.setAccountAmountUndepositableBefore(toFundAccount.getAccountAmountUndepositable());
        journal.setAccountAmountUndepositableAfterwards(toFundAccount.getAccountAmountUndepositable());

        return journal;
    }
}
